﻿#if Net35
namespace System
{
    using System;
    using System.Diagnostics;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.Threading;

    #pragma warning disable 0420

    public enum LazyThreadSafetyMode
    {
        None,
        PublicationOnly,
        ExecutionAndPublication
    }

    [HostProtection(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
    [Serializable]
    public class Lazy<T>
    {
        [Serializable]
        private class Boxed
        {
            internal T _value;

            internal Boxed(T value)
            {
                this._value = value;
            }
        }

        private class LazyInternalExceptionHolder
        {
            internal Exception _exception;

            internal LazyInternalExceptionHolder(Exception ex)
            {
                this._exception = ex;
            }
        }

        private static Func<T> PUBLICATION_ONLY_OR_ALREADY_INITIALIZED = () => default(T);

        private volatile object _boxed;

        [NonSerialized]
        private Func<T> _valueFactory;

        [NonSerialized]
        private readonly object _threadSafeObj;

        internal T ValueForDebugDisplay
        {
            get
            {
                if (!this.IsValueCreated)
                {
                    return default(T);
                }

                return ((Lazy<T>.Boxed)this._boxed)._value;
            }
        }

        internal LazyThreadSafetyMode Mode
        {
            get
            {
                if (this._threadSafeObj == null)
                {
                    return LazyThreadSafetyMode.None;
                }

                if (this._threadSafeObj == Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED)
                {
                    return LazyThreadSafetyMode.PublicationOnly;
                }

                return LazyThreadSafetyMode.ExecutionAndPublication;
            }
        }

        internal bool IsValueFaulted
        {
            get
            {
                return this._boxed is Lazy<T>.LazyInternalExceptionHolder;
            }
        }

        public bool IsValueCreated
        {
            get
            {
                return this._boxed != null && this._boxed is Lazy<T>.Boxed;
            }
        }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public T Value
        {
            get
            {
                if (this._boxed == null)
                {
                    return this.LazyInitValue();
                }

                Lazy<T>.Boxed boxed = this._boxed as Lazy<T>.Boxed;
                if (boxed != null)
                {
                    return boxed._value;
                }

                Lazy<T>.LazyInternalExceptionHolder lazyInternalExceptionHolder = 
                    this._boxed as Lazy<T>.LazyInternalExceptionHolder;
                throw lazyInternalExceptionHolder._exception;
            }
        }

        public Lazy()
            : this(LazyThreadSafetyMode.ExecutionAndPublication)
        {
        }

        public Lazy(Func<T> valueFactory)
            : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)
        {
        }

        public Lazy(bool isThreadSafe)
            : this(isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication :
                    LazyThreadSafetyMode.None)
        {
        }

        public Lazy(LazyThreadSafetyMode mode)
        {
            this._threadSafeObj = Lazy<T>.GetObjectFromMode(mode);
        }

        public Lazy(Func<T> valueFactory, bool isThreadSafe) : this(
            valueFactory,
            isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
        {
        }

        public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode)
        {
            if (valueFactory == null)
            {
                throw new ArgumentNullException("valueFactory");
            }

            this._threadSafeObj = Lazy<T>.GetObjectFromMode(mode);
            this._valueFactory = valueFactory;
        }

        private static object GetObjectFromMode(LazyThreadSafetyMode mode)
        {
            if (mode == LazyThreadSafetyMode.ExecutionAndPublication)
            {
                return new object();
            }

            if (mode == LazyThreadSafetyMode.PublicationOnly)
            {
                return Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;
            }

            if (mode != LazyThreadSafetyMode.None)
            {
                throw new ArgumentOutOfRangeException("mode", "Lazy_ctor_ModeInvalid");
            }

            return null;
        }

        [OnSerializing]
        private void OnSerializing(StreamingContext context)
        {
        }

        public override string ToString()
        {
            if (!this.IsValueCreated)
            {
                return "Lazy_ToString_ValueNotCreated";
            }

            T value = this.Value;
            return value.ToString();
        }

        private T LazyInitValue()
        {
            Lazy<T>.Boxed boxed = null;
            LazyThreadSafetyMode mode = this.Mode;
            if (mode == LazyThreadSafetyMode.None)
            {
                boxed = this.CreateValue();
                this._boxed = boxed;
            }
            else
            {
                if (mode == LazyThreadSafetyMode.PublicationOnly)
                {
                    boxed = this.CreateValue();
                    if (Interlocked.CompareExchange(ref this._boxed, boxed, null) != null)
                    {
                        boxed = (Lazy<T>.Boxed)this._boxed;
                    }
                }
                else
                {
                    lock (this._threadSafeObj)
                    {
                        if (this._boxed == null)
                        {
                            boxed = this.CreateValue();
                            this._boxed = boxed;
                        }
                        else
                        {
                            boxed = this._boxed as Lazy<T>.Boxed;
                            if (boxed == null)
                            {
                                Lazy<T>.LazyInternalExceptionHolder lazyInternalExceptionHolder = this._boxed as Lazy<T>.LazyInternalExceptionHolder;
                                throw lazyInternalExceptionHolder._exception;
                            }
                        }
                    }
                }
            }

            return boxed._value;
        }

        private Lazy<T>.Boxed CreateValue()
        {
            Lazy<T>.Boxed result = null;
            LazyThreadSafetyMode mode = this.Mode;
            if (this._valueFactory != null)
            {
                try
                {
                    if (mode != LazyThreadSafetyMode.PublicationOnly && 
                        this._valueFactory == Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED)
                    {
                        throw new InvalidOperationException("Lazy_Value_RecursiveCallsToValue");
                    }

                    Func<T> valueFactory = this._valueFactory;
                    if (mode != LazyThreadSafetyMode.PublicationOnly)
                    {
                        this._valueFactory = Lazy<T>.PUBLICATION_ONLY_OR_ALREADY_INITIALIZED;
                    }

                    result = new Lazy<T>.Boxed(valueFactory());
                    return result;
                }
                catch (Exception ex)
                {
                    if (mode != LazyThreadSafetyMode.PublicationOnly)
                    {
                        this._boxed = new Lazy<T>.LazyInternalExceptionHolder(ex);
                    }

                    throw;
                }
            }

            try
            {
                result = new Lazy<T>.Boxed((T)((object)Activator.CreateInstance(typeof(T))));
            }
            catch (MissingMethodException)
            {
                Exception ex2 = new MissingMemberException("Lazy_CreateValue_NoParameterlessCtorForT");
                if (mode != LazyThreadSafetyMode.PublicationOnly)
                {
                    this._boxed = new Lazy<T>.LazyInternalExceptionHolder(ex2);
                }

                throw ex2;
            }

            return result;
        }
    }

    #pragma warning restore 0420
}
#endif